﻿#include "DBFlushThread.h"
#include "base/core/com_define.h"
#include "base/core/os/time.h"
#include "base/core/os/string.h"
#include "DBProxyService.h"
#include "DBOption/Redis/RedisCommand.h"
#include "DBOption/Utility/ConnectorHelp.h"
#include "Common/AppendContext.h"
#include "DBCommon.h"

DBFlushThread::ProcessFunction DBFlushThread::ms_FunctionTable[DB_FLUSH_EVENT_COUNT] = {0};

DBFlushThread::DBFlushThread() 
{
	ms_FunctionTable[DB_FLUSH_UPDATE_CHARACTER_DATA] = &DBFlushThread::_flush_character;
	ms_FunctionTable[DB_FLUSH_DELETE_CHARACTER_DATA] = &DBFlushThread::_delete_character;

    m_flushFrequency = 100;
    m_threadExitFlag = false;
    m_threadWorkingFlag = false;
    m_dbname = "";
    m_groupId = 0;
}

DBFlushThread::~DBFlushThread() 
{}

bool DBFlushThread::Start(const DB_FLUSH_START_PARAM& dbFlushStartParam) 
{
    bool result  = false;
    bool retCode = false;

    m_param = dbFlushStartParam;

    MMOLOG_ASSERT_EXIT(m_param.groupId != 0);
    MMOLOG_ASSERT_EXIT(!m_param.mysqlParam.database.empty());

    m_groupId = m_param.groupId;
    m_dbname = m_param.mysqlParam.database.c_str();
    m_flushFrequency = m_param.mysqlParam.flushFrequency;

    retCode = m_thread.RunThread(_ThreadFunctionProxy, this);
    MMOLOG_PROCESS_ERROR(retCode);

    result = true;
Exit0:
    return result;
}

bool DBFlushThread::Stop() 
{
    m_threadExitFlag = true;
    while (m_threadWorkingFlag) 
	{
        Time::Sleep(2);
    }
    m_thread.StopThread(Thread::WAIT_SELF_EXIT);
    return true;
}

void DBFlushThread::_ThreadFunctionProxy(void *param) 
{
    (reinterpret_cast<DBFlushThread*>(param))->_ThreadFunction();
}

bool DBFlushThread::_ThreadFunction()
{
    bool result  = false;
    bool retCode = false;
	std::queue<EVENT_DATA> eventQueue;

    m_threadWorkingFlag = true;

	mysql_thread_init();

    retCode = _ConnectDatabase();
    MMOLOG_PROCESS_ERROR(retCode);

    while (!m_threadExitFlag) 
	{
        for (int i = 0; i < DB_FLUSH_EVENT_COUNT; ++i) 
		{
            _ProcessFlush(i, false);
            Time::Sleep(500);
        }
        Time::Sleep(500);
    }
    for (int i = 0; i < DB_FLUSH_EVENT_COUNT; ++i) 
	{
        _ProcessFlush(i, true);
    }
    result = true;
Exit0:
	mysql_thread_end();
    m_threadWorkingFlag = false;
    return result;
}

bool DBFlushThread::_ConnectDatabase() 
{
    bool result = false;
    bool retCode = false;

	retCode = ConnectToRedis("DBFlush", &m_redisConnector, m_param.redisParam, &m_threadExitFlag);
	MMOLOG_PROCESS_ERROR(retCode);

    retCode = ConnectToMySQL("DBFlush", &m_mysqlConnector, m_param.mysqlParam, &m_threadExitFlag);
    MMOLOG_PROCESS_ERROR(retCode);
    result = true;
Exit0:
    return result;
}

bool DBFlushThread::_SetUpdateToMysqlFlag(DB_FLUSH_EVENT_TYPE type, CharIDType id, DBFLUSH_UPDATE_STATE charUpdateState)
{
	bool result = false;
	redisReply* reply = nullptr;
	int length = snprintf(m_command, countof(m_command), DB_REDIS_FLUSH_STATE_STR, m_dbname, m_groupId, (uint32_t)type);
	MMOLOG_ASSERT_EXIT((length > 0) && (length < (int)countof(m_command)));

	if (charUpdateState != UPDATE_STATE_DELETE)
	{
		reply = m_redisConnector.Execute("HSET %s %lu %d", m_command, id, (int)charUpdateState);
		MMOLOG_PROCESS_ERROR(reply && reply->type == REDIS_REPLY_INTEGER);
	}
	else 
	{
		reply = m_redisConnector.Execute("HDEL %s %lu", m_command, id);
		MMOLOG_PROCESS_ERROR(reply && reply->type == REDIS_REPLY_INTEGER);
	}

	result = true;
Exit0:
	MMO_FREE_REPLY(reply);
	return result;
}

bool DBFlushThread::_ProcessError(const char* command, int currentType, CharIDType id)
{
    redisReply* reply = nullptr;

    LogErrFmtPrint(
					"flush db failed group %u type %d, id %lu\n",
					m_param.groupId, currentType, id
				  );
	reply = m_redisConnector.Execute("RPUSH %s %lu", command, id);
    MMO_FREE_REPLY(reply);

    return true;
}

bool DBFlushThread::_ProcessGroupData(int currentType, bool all) 
{
    bool result  = false;
    bool retCode = false;
    int length = 0;
    redisReply* reply = nullptr;

    length = snprintf(m_groupCommand, countof(m_groupCommand), DB_REDIS_FLUSH_LIST_STR, m_dbname, m_groupId, currentType);
    MMOLOG_ASSERT_EXIT((length > 0) && (length < (int)countof(m_groupCommand)));

    for (uint32_t currentFlushCount = 0; all || (currentFlushCount < m_flushFrequency && !m_threadExitFlag); ++currentFlushCount)
	{
		reply = m_redisConnector.Execute("LPOP %s", m_groupCommand);
        if (!reply || reply->type == REDIS_REPLY_NIL || reply->type == REDIS_REPLY_ERROR)
            break;
        MMOLOG_ASSERT_EXIT(reply->type == REDIS_REPLY_STRING);

		CharIDType id = (CharIDType)atol(reply->str);
        freeReplyObject(reply);
        reply = nullptr;

        retCode = (this->*ms_FunctionTable[currentType])(id);
        if (!retCode) 
		{
			_ProcessError(m_groupCommand, currentType, id);
            if (all) break;
        }
        if (!all) { Time::Sleep(100); }
    }

    result = true;
Exit0:
    MMO_FREE_REPLY(reply);
    return result;
}

bool DBFlushThread::_ProcessFlush(int currentType, bool all) 
{
    bool result  = false;
    bool retCode = false;

    if (m_param.dbName == DBFLUSH_DBNAME_GROUP) 
	{
        retCode = _ProcessGroupData(currentType, all);
        MMOLOG_ASSERT_EXIT(retCode);
    } 
	else if (m_param.dbName == DBFLUSH_DBNAME_ZONE) 
	{
    }
    result = true;
Exit0:

    return result;
}

bool DBFlushThread::_GetDataRedis(const string& key, string& data) 
{
    bool result = false;
    redisReply* reply = NULL;

    reply = m_redisConnector.Execute("GET %s", key.c_str());
    MMOLOG_ASSERT_EXIT(reply);
    if (reply->type == REDIS_REPLY_STRING) {
        data.assign(reply->str, reply->len);
    }
    result = true;
Exit0:
    MMO_FREE_REPLY(reply);
    return result;
}

bool DBFlushThread::_SetDataRedisExpire(const string& key) 
{
    bool result = false;
    redisReply* reply = NULL;

    reply = m_redisConnector.Execute("EXPIRE %s %u", key.c_str(), DB_REDIS_EXPIRE_TIME);
    MMOLOG_ASSERT_EXIT(reply && reply->type == REDIS_REPLY_INTEGER);

    result = true;
Exit0:
    MMO_FREE_REPLY(reply);
    return result;
}

bool DBFlushThread::_CreateMysqlData(const string& key, const string& data, string& sqlExec) 
{
    bool result = false;
    string table, str, field, fieldData, sqlValue;
    uint32_t len = 0;
    vector<string> vecStr, vecField;

    MMOLOG_PROCESS_ERROR(!key.empty());
    MMOLOG_PROCESS_ERROR(!data.empty());

    CommonApi::SplitStr(key, ":", &vecStr);
    MMOLOG_PROCESS_ERROR(vecStr.size() == 3);
    table = vecStr[0];
    sqlExec = "INSERT INTO " + table + "(" + vecStr[1] + ",";
    sqlValue = "VALUES(\"" + vecStr[2] + "\",";

    CommonApi::SplitStr(data, ",", &vecStr);
    for (int i = 0; i < (int)vecStr.size(); i++)
    {
        str = vecStr[i];
        CommonApi::SplitStr(str, ":", &vecField);
        MMOLOG_PROCESS_ERROR(vecField.size() == 2);
        field = vecField[0];
        fieldData = vecField[1];
        sqlExec.append(field);
        m_mysqlConnector.EscapeString(m_command, fieldData.c_str(), (uint32_t)fieldData.length(), len);
        fieldData.assign(m_command, len);
        fieldData = "\"" + fieldData + "\"";
        sqlValue.append(fieldData);
        if (i != ((int)vecStr.size() - 1))
        {
            sqlExec.append(",");
            sqlValue.append(",");
        }
    }

    sqlExec.append(") ");
    sqlValue.append(")");
    sqlExec.append(sqlValue);
    sqlExec.append(";");

    result = true;
Exit0:
    return result;
}

bool DBFlushThread::_UpdateMysqlData(const string& key, const string& data, string& sqlExec) 
{
    bool result = false;
    uint32_t len = 0;
    string table, primaryKey, primaryKeyData, str, field, fieldData;

    vector<string> vecStr, vecField;
    MMOLOG_PROCESS_ERROR(!key.empty());
    MMOLOG_PROCESS_ERROR(!data.empty());

    CommonApi::SplitStr(key, ":", &vecStr);
    MMOLOG_PROCESS_ERROR(vecStr.size() == 3);
    table = vecStr[0];
    primaryKey = vecStr[1];
    primaryKeyData = vecStr[2];
    m_mysqlConnector.EscapeString(m_command, primaryKeyData.c_str(), (uint32_t)primaryKeyData.length(), len);
    primaryKeyData.assign(m_command, len);
    sqlExec = "UPDATE " + table + " SET ";

    CommonApi::SplitStr(data, ",", &vecStr);
    for (int i = 0; i < (int)vecStr.size(); i++) {
        str = vecStr[i];
        CommonApi::SplitStr(str, ":", &vecField);
        MMOLOG_PROCESS_ERROR(vecField.size() == 2);
        field = vecField[0];
        fieldData = vecField[1];
        sqlExec.append(field);
        sqlExec.append("=");
        m_mysqlConnector.EscapeString(m_command, fieldData.c_str(), (uint32_t)fieldData.length(), len);
        fieldData.assign(m_command, len);
        fieldData = "\"" + fieldData + "\"";
        sqlExec.append(fieldData);

        if (i != ((int)vecStr.size() - 1)) {
            sqlExec.append(",");
        }
    }
    sqlExec.append(" WHERE ");
    sqlExec.append(primaryKey);
    sqlExec.append(" = \"");
    sqlExec.append(primaryKeyData);
    sqlExec.append("\";");


    result = true;
Exit0:
    return result;
}

bool DBFlushThread::_DeleteMysqlData(const string& key, const string& data, string& sqlExec) 
{
    bool result = false;
    string table, primaryKey, primaryKeyData, str, field, fieldData;
    uint32_t len = 0;
    vector<string> vecStr, vecField;

    MMOLOG_PROCESS_ERROR(!key.empty());

    CommonApi::SplitStr(key, ":", &vecStr);
    MMOLOG_PROCESS_ERROR(vecStr.size() == 3);
    table = vecStr[0];
    primaryKey = vecStr[1];
    primaryKeyData = vecStr[2];
    sqlExec = "DELETE FROM " + table + " WHERE ";
    sqlExec.append(primaryKey);
    sqlExec.append("=");
    m_mysqlConnector.EscapeString(m_command, primaryKeyData.c_str(), (uint32_t)primaryKeyData.length(), len);
    primaryKeyData.assign(m_command, len);
    primaryKeyData = "\"" + primaryKeyData + "\"";
    sqlExec.append(primaryKeyData);
    sqlExec.append(";");
    result = true;
Exit0:
    return result;
}

bool DBFlushThread::_GetCharacterDataRedis(CharIDType charId, CharacterDBInfo* retCharInfo)
{
	bool result = false;
	bool retCode = false;
	int length = 0;
	redisReply* reply = NULL;

	retCharInfo->set_charid(charId);
	retCharInfo->set_groupid(g_GetDBProxyService()->GetZoneId());

	length = snprintf(m_command, countof(m_command), "group:%u:character:%lu:data",m_groupId, charId);
	MMOLOG_ASSERT_EXIT((length > 0) && (length < (int)countof(m_command)));

	reply = m_redisConnector.Execute("HGET %s base", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	MMO_PROCESS_ERROR(reply->type != REDIS_REPLY_NIL);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterDBBaseInfo* charDBBaseInfo = retCharInfo->mutable_basedata();
		retCode = charDBBaseInfo->ParseFromArray(reply->str, reply->len);
		MMOLOG_ASSERT_EXIT(retCode);
	}
	MMOLOG_PROCESS_ERROR(retCharInfo->has_basedata());
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s item", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterDBPackageData* charDBPackageData = retCharInfo->mutable_itemdata();
		retCode = charDBPackageData->ParseFromArray(reply->str, reply->len);
		MMOLOG_ASSERT_EXIT(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s task", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterDBTaskData* charDBTaskData = retCharInfo->mutable_taskdata();
		retCode = charDBTaskData->ParseFromArray(reply->str, reply->len);
		MMOLOG_ASSERT_EXIT(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s skill", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterDBSkillData* charDBSkillData = retCharInfo->mutable_skilldata();
		retCode = charDBSkillData->ParseFromArray(reply->str, reply->len);
		MMOLOG_ASSERT_EXIT(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s extend", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterDBExtendData* charDBExtendData = retCharInfo->mutable_extenddata();
		retCode = charDBExtendData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s equip", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterDBEquipData* charDBEquipData = retCharInfo->mutable_equipdata();
		retCode = charDBEquipData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s duplicate", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterDBDuplicateData* charDBDuplicateData = retCharInfo->mutable_duplicatedata();
		retCode = charDBDuplicateData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);


	reply = m_redisConnector.Execute("HGET %s deputy", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterDeputyDBData* charDeputyDBData = retCharInfo->mutable_deputydata();
		retCode = charDeputyDBData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);


	reply = m_redisConnector.Execute("HGET %s achieve", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterAchievementDBData* charAchieveDBData = retCharInfo->mutable_achievementdata();
		retCode = charAchieveDBData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

//	reply = m_redisConnector.Execute("HGET %s pet", m_command);
//	MMOLOG_ASSERT_EXIT(reply);
//	if (reply->type == REDIS_REPLY_STRING)
//	{
//		CharacterPetData* charPetData = retCharInfo->mutable_petdata();
//		retCode = charPetData->ParseFromArray(reply->str, reply->len);
//		MMOLOG_PROCESS_ERROR(retCode);
//	}
//	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s arena", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterArenaData* charArenaData = retCharInfo->mutable_arenadata();
		retCode = charArenaData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s treasure", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterTreasureData* charTreasureData = retCharInfo->mutable_treasuredata();
		retCode = charTreasureData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s chat", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterChatData* chatData = retCharInfo->mutable_chatdata();
		retCode = chatData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s welfare", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		::ProtoBuf::CharacterDBOperateWelfareInfo* welfareData = retCharInfo->mutable_operatewelfareinfo();
		retCode = welfareData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s title", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterTitleSaveData* titiData = retCharInfo->mutable_titledata();
		retCode = titiData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s operatelimit", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterDBOperateLimitInfo* operateLimitData = retCharInfo->mutable_operatelimitdata();
		retCode = operateLimitData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s huoyuedu", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		CharacterDBHuoYueDuInfo* huoYueDuData = retCharInfo->mutable_huoyuedudata();
		retCode = huoYueDuData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s buff", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		::ProtoBuf::CharacterBuffData* buffData = retCharInfo->mutable_buffdata();
		retCode = buffData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s horse", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		::ProtoBuf::CharacterHorseInfo* horseData = retCharInfo->mutable_horsedata();
		retCode = horseData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s god", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		::ProtoBuf::CharacterDBGodInfo* godData = retCharInfo->mutable_goddata();
		retCode = godData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s pandos", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		::ProtoBuf::CharacterDBPandosInfo* pandosData = retCharInfo->mutable_pandosdata();
		retCode = pandosData->ParseFromArray(reply->str, reply->len);
		MMOLOG_PROCESS_ERROR(retCode);
	}
	MMO_FREE_REPLY(reply);

	reply = m_redisConnector.Execute("HGET %s playeractive", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		::ProtoBuf::CharacterDBActiveInfo* pActiveData = retCharInfo->mutable_playeractiveinfo();
		retCode = pActiveData->ParseFromArray(reply->str, reply->len);
	}
	MMO_FREE_REPLY(reply);
	reply = m_redisConnector.Execute("HGET %s paydata", m_command);
	MMOLOG_ASSERT_EXIT(reply);
	if (reply->type == REDIS_REPLY_STRING)
	{
		::ProtoBuf::CharacterPayData* pPayData = retCharInfo->mutable_cpaydata();
		retCode = pPayData->ParseFromArray(reply->str, reply->len);
	}
	MMO_FREE_REPLY(reply);

	result = true;
Exit0:
	MMO_FREE_REPLY(reply);
	return result;
}

bool DBFlushThread::_SetCharacterDataMysql(CharIDType charId, const CharacterDBInfo& charInfo)
{
	bool result = false;
	bool retCode = false;
	bool hasData = false;
	int length = 0;
	APPEND_CONTEXT context;

	context.connector = &m_mysqlConnector;
	context.writePos = m_command;
	context.freeSize = countof(m_command);

	length = snprintf(m_command, countof(m_command), "UPDATE charInfo SET ");
	MMOLOG_ASSERT_EXIT((length > 0) && (length < context.freeSize));
	context.writePos += length;
	context.freeSize -= length;
	if (charInfo.has_basedata())
	{
		retCode = AppendString(&context, "m_base_data = \'", charInfo.basedata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}
	if (charInfo.has_itemdata()) 
	{
		retCode = AppendString(&context, "m_item_data = \'", charInfo.itemdata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}
	if (charInfo.has_taskdata())
	{
		retCode = AppendString(&context, "m_task_data = \'", charInfo.taskdata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}
	if (charInfo.has_skilldata())
	{
		retCode = AppendString(&context, "m_skill_data = \'", charInfo.skilldata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}
	if (charInfo.has_extenddata()) 
	{
		retCode = AppendString(&context, "m_extend_data = \'", charInfo.extenddata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}
	if (charInfo.basedata().has_logintime() && charInfo.basedata().logintime() > 0)
	{
		char tameStamp[32] = { 0 };
		Date date = Time::GetLocalDate(charInfo.basedata().logintime());
		snprintf(tameStamp, countof(tameStamp), "%04d-%02d-%02d %02d:%02d:%02d", date.year, date.mon, date.mday, date.hour, date.min, date.sec);
		retCode = AppendString(&context, "m_login_time = \'", tameStamp, "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}
	if (charInfo.basedata().has_logouttime() && charInfo.basedata().logouttime() > 0)
	{
		char tameStamp[32] = { 0 };
		Date date = Time::GetLocalDate(charInfo.basedata().logouttime());
		snprintf(tameStamp, countof(tameStamp), "%04d-%02d-%02d %02d:%02d:%02d", date.year, date.mon, date.mday, date.hour, date.min, date.sec);
		retCode = AppendString(&context, "m_logout_time = \'", tameStamp, "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}
	if (charInfo.has_equipdata())
	{
		retCode = AppendString(&context, "m_equip_data = \'", charInfo.equipdata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_duplicatedata())
	{
		retCode = AppendString(&context, "m_duplicate_data = \'", charInfo.duplicatedata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_deputydata())
	{
		retCode = AppendString(&context, "m_deputy_data = \'", charInfo.deputydata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_achievementdata())
	{
		retCode = AppendString(&context, "m_achieve_data = \'", charInfo.achievementdata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	//if (charInfo.has_petdata())
	//{
	//	retCode = AppendString(&context, "m_pet_data = \'", charInfo.petdata().SerializeAsString(), "\',");
	//	MMOLOG_PROCESS_ERROR(retCode);
	//	hasData = true;
	//}

	if (charInfo.has_arenadata())
	{
		retCode = AppendString(&context, "m_arena_data = \'", charInfo.arenadata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_treasuredata())
	{
		retCode = AppendString(&context, "m_treasure_data = \'", charInfo.treasuredata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_chatdata())
	{
		retCode = AppendString(&context, "m_chat_data = \'", charInfo.chatdata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_operatewelfareinfo())
	{
		retCode = AppendString(&context, "m_welfare_data = \'", charInfo.operatewelfareinfo().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_titledata())
	{
		retCode = AppendString(&context, "m_title_data = \'", charInfo.titledata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_operatelimitdata())
	{
		retCode = AppendString(&context, "m_operatelimit_data = \'", charInfo.operatelimitdata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_huoyuedudata())
	{
		retCode = AppendString(&context, "m_huoyuedu_data = \'", charInfo.huoyuedudata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_horsedata())
	{
		retCode = AppendString(&context, "m_horse_data = \'", charInfo.horsedata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_buffdata())
	{
		retCode = AppendString(&context, "m_buff_data = \'", charInfo.buffdata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_goddata())
	{
		retCode = AppendString(&context, "m_god_data = \'", charInfo.goddata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_pandosdata())
	{
		retCode = AppendString(&context, "m_pandos_data = \'", charInfo.pandosdata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_playeractiveinfo())
	{
		retCode = AppendString(&context, "m_active_data = \'", charInfo.playeractiveinfo().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}

	if (charInfo.has_cpaydata())
	{
		retCode = AppendString(&context, "m_pay_data = \'", charInfo.cpaydata().SerializeAsString(), "\',");
		MMOLOG_PROCESS_ERROR(retCode);
		hasData = true;
	}


	MMO_PROCESS_SUCCESS(!hasData);
	--context.writePos;
	++context.freeSize;
	length = snprintf(context.writePos, context.freeSize, " WHERE m_id = %lu", charId);
	MMOLOG_ASSERT_EXIT((length > 0) && (length < context.freeSize));
	context.writePos += length;
	context.freeSize -= length;

	retCode = m_mysqlConnector.Execute(m_command, countof(m_command) - context.freeSize);
	MMOLOG_PROCESS_ERROR(retCode);

Exit1:
	result = true;
Exit0:
	return result;
}

bool DBFlushThread::_DelCharacterDataMysql(CharIDType charId)
{
	bool result = false;
	bool retCode = false;
	int length = 0;
	APPEND_CONTEXT context;

	context.connector = &m_mysqlConnector;
	context.writePos = m_command;
	context.freeSize = countof(m_command);

	length = snprintf(context.writePos, context.freeSize, "DELETE FROM charInfo WHERE m_id = %lu", charId);
	MMOLOG_PROCESS_ERROR((length > 0) && (length < context.freeSize));

	context.writePos += length;
	context.freeSize -= length;

	retCode = m_mysqlConnector.Execute(m_command, countof(m_command) - context.freeSize);
	MMOLOG_PROCESS_ERROR(retCode);

	result = true;
Exit0:
	return result;
}

bool DBFlushThread::_SetCharacterDataExpire(CharIDType charId)
{
	bool result = false;
	int length = 0;
	redisReply* reply = NULL;

	length = snprintf(m_command, countof(m_command), "group:%u:character:%lu:data",m_groupId, charId);
	MMOLOG_ASSERT_EXIT((length > 0) && (length < (int)countof(m_command)));

	reply = m_redisConnector.Execute("EXPIRE %s %u", m_command, DB_REDIS_EXPIRE_TIME);
	MMOLOG_PROCESS_ERROR(reply && reply->type == REDIS_REPLY_INTEGER);

	result = true;
Exit0:
	MMO_FREE_REPLY(reply);
	return result;
}

bool DBFlushThread::_flush_character(CharIDType id)
{
	bool result = false;
	bool retCode = false;
	CharacterDBInfo charInfo;

	retCode = _GetCharacterDataRedis(id, &charInfo);
	MMOLOG_PROCESS_ERROR(retCode);

	retCode = _SetCharacterDataMysql(id, charInfo);
	MMOLOG_PROCESS_ERROR(retCode);

	retCode = _SetCharacterDataExpire(id);
	MMOLOG_PROCESS_ERROR(retCode);

	result = true;
Exit0:
	_SetUpdateToMysqlFlag(DB_FLUSH_UPDATE_CHARACTER_DATA, id, UPDATE_STATE_DELETE);
	return result;
}

bool DBFlushThread::_delete_character(CharIDType id)
{
	bool result = false;
	bool retCode = false;
	LogInfoFmtPrint("DBFlushThread::_delete_character success, cid:%lu", id);

	retCode = _DelCharacterDataRedis(id);
	MMOLOG_PROCESS_ERROR(retCode);

	retCode = _DelCharacterDataMysql(id);
	MMOLOG_PROCESS_ERROR(retCode);

	result = true;
Exit0:
	_SetUpdateToMysqlFlag(DB_FLUSH_DELETE_CHARACTER_DATA, id, UPDATE_STATE_DELETE);
	return result;
}

bool DBFlushThread::_DelCharacterDataRedis(CharIDType charId)
{
	bool result = false;
	int length = 0;
	redisReply* reply = nullptr;

	length = snprintf(m_command, countof(m_command), "group:%u:character:%lu:data", m_groupId, charId);
	MMOLOG_ASSERT_EXIT((length > 0) && (length < (int)countof(m_command)));

	reply = m_redisConnector.Execute("DEL %s", m_command);
	MMOLOG_PROCESS_ERROR(reply && reply->type == REDIS_REPLY_INTEGER);

	freeReplyObject(reply);
	reply = nullptr;

	result = true;
Exit0:
	MMO_FREE_REPLY(reply);
	return result;
}

